#include "graphicslayersetdialog.h"

#include "ItemModels/LayerSetModel.h"

#include "LeGraphicsView/LeGraphicsItem.h"
#include "LeGraphicsView/LeGraphicsPalette.h"
#include "LeGraphicsView/LeGraphicsScene.h"

#include "Widgets/brushstylecombobox.h"
#include "Widgets/brushstylepickerwidget.h"
#include "Widgets/colorcombobox.h"
#include "Widgets/ColorPickerWidget.h"
#include "Widgets/modelitembrushdelegate.h"
#include "Widgets/ModelItemCheckBoxDelegate.h"
#include "Widgets/penstylecombobox.h"

#include <QCheckBox>
#include <QComboBox>
#include <QDialogButtonBox>
#include <QDoubleSpinBox>
#include <QFormLayout>
#include <QGridLayout>
#include <QGroupBox>
#include <QHeaderView>
#include <QPushButton>
#include <QSettings>
#include <QTabWidget>
#include <QTableView>
#include <QVBoxLayout>

GraphicsLayerSetDialog::GraphicsLayerSetDialog(QWidget *parent):
    QDialog(parent),
    m_tabWidget(new QTabWidget),
    m_view(new QTableView),
    m_model(nullptr),
    m_scene(nullptr)
{
    setLayout(new QVBoxLayout());
    m_tabWidget->addTab(createLayerSetTab(), "Layer Set");
    m_tabWidget->addTab(createOtherLayersTab(), "Decoration");
    m_tabWidget->addTab(createAdvancedTab(), "Advanced");
    layout()->addWidget(m_tabWidget);
    layout()->addWidget(createButtonBox());

    restoreGuiState();
}

GraphicsLayerSetDialog::~GraphicsLayerSetDialog()
{
    saveGuiState();
}

void GraphicsLayerSetDialog::setData(LayerSetModel *model, LeGraphicsScene *scene)
{
    m_buttonBox->disconnect(this);

    m_view->setModel(nullptr);

    m_model = model;
    setApplyResetEnabled(true);
    m_view->setModel(m_model);

    m_view->hideColumn(LayerSetModel::VisibleColumn);
    m_view->setItemDelegateForColumn(LayerSetModel::EnabledColumn,
                                     new ModelItemCheckBoxDelegate(m_view));
    m_view->setItemDelegateForColumn(LayerSetModel::PadVisibleColumn,
                                     new ModelItemCheckBoxDelegate(m_view));
    m_view->setItemDelegateForColumn(LayerSetModel::PadBrushColumn,
                                     new ModelItemBrushDelegate(padPath(), m_view));
    m_view->setItemDelegateForColumn(LayerSetModel::PinVisibleColumn,
                                     new ModelItemCheckBoxDelegate(m_view));
    m_view->setItemDelegateForColumn(LayerSetModel::PinBrushColumn,
                                     new ModelItemBrushDelegate(pinPath(), m_view));
    m_view->setItemDelegateForColumn(LayerSetModel::FiducialVisibleColumn,
                                     new ModelItemCheckBoxDelegate(m_view));
    m_view->setItemDelegateForColumn(LayerSetModel::FiducialBrushColumn,
                                     new ModelItemBrushDelegate(fiducialPath(), m_view));
    m_view->setItemDelegateForColumn(LayerSetModel::HoleVisibleColumn,
                                     new ModelItemCheckBoxDelegate(m_view));
    m_view->setItemDelegateForColumn(LayerSetModel::HoleBrushColumn,
                                     new ModelItemBrushDelegate(holePath(), m_view));
    m_view->setItemDelegateForColumn(LayerSetModel::SlotVisibleColumn,
                                     new ModelItemCheckBoxDelegate(m_view));
    m_view->setItemDelegateForColumn(LayerSetModel::SlotBrushColumn,
                                     new ModelItemBrushDelegate(slotPath(), m_view));
    m_view->setItemDelegateForColumn(LayerSetModel::FillVisibleColumn,
                                     new ModelItemCheckBoxDelegate(m_view));
    m_view->setItemDelegateForColumn(LayerSetModel::FillBrushColumn,
                                     new ModelItemBrushDelegate(fillPath(), m_view));
    m_view->setItemDelegateForColumn(LayerSetModel::TraceVisibleColumn,
                                     new ModelItemCheckBoxDelegate(m_view));
    m_view->setItemDelegateForColumn(LayerSetModel::TraceBrushColumn,
                                     new ModelItemBrushDelegate(tracePath(), m_view));
    m_view->setItemDelegateForColumn(LayerSetModel::TextVisibleColumn,
                                     new ModelItemCheckBoxDelegate(m_view));
    m_view->setItemDelegateForColumn(LayerSetModel::TextBrushColumn,
                                     new ModelItemBrushDelegate(textPath(), m_view));
    m_view->setAlternatingRowColors(true);
    m_view->setSelectionBehavior(QTableView::SelectItems);
    m_view->setSelectionMode(QTableView::ExtendedSelection);
    m_view->setEditTriggers(QTableView::DoubleClicked |
                            QTableView::SelectedClicked |
                            QTableView::EditKeyPressed);
    m_view->setAutoScroll(false);
    m_view->setVerticalScrollMode(QTableView::ScrollPerItem);
    m_view->setHorizontalScrollMode(QTableView::ScrollPerItem);

    auto header = m_view->horizontalHeader();
    header->setSectionResizeMode(QHeaderView::ResizeToContents);
    header->setSectionResizeMode(LayerSetModel::NameColumn, QHeaderView::Stretch);

    connect(m_buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked,
            this, &GraphicsLayerSetDialog::accept);
    connect(m_buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked,
            this, &GraphicsLayerSetDialog::reject);
    connect(m_buttonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked,
            this, &GraphicsLayerSetDialog::apply);
    connect(m_buttonBox->button(QDialogButtonBox::Reset), &QPushButton::clicked,
            this, &GraphicsLayerSetDialog::reset);

    m_scene = scene;
    m_palette = m_scene->graphicsPalette();
    // TODO: apply colors to combo boxes
}

void GraphicsLayerSetDialog::accept()
{
    apply();
    QDialog::accept();
}

void GraphicsLayerSetDialog::reject()
{
    reset();
    QDialog::reject();
}

void GraphicsLayerSetDialog::reset()
{

}

void GraphicsLayerSetDialog::apply()
{
    m_scene->setGraphicsPalette(m_palette);
}

void GraphicsLayerSetDialog::setApplyResetEnabled(bool enabled)
{
    m_buttonBox->button(QDialogButtonBox::Apply)->setEnabled(enabled);
    m_buttonBox->button(QDialogButtonBox::Reset)->setEnabled(enabled);
}

QWidget *GraphicsLayerSetDialog::createLayerSetTab()
{
    return m_view;
}

QWidget *GraphicsLayerSetDialog::createOtherLayersTab()
{
    QWidget *widget = new QWidget();
//    auto layout = new QGridLayout();
//    widget->setLayout(layout);

//    int row = 0;
//    int col = 0;
//    layout->addWidget(createBackgroundGroupBox(), row, col++, 1, 1);
//    layout->addWidget(createHighlightGroupBox(), row, col++, 1, 1);
//    layout->addWidget(createGoodMarkGroupBox(), row, col++, 1, 1);
//    row++; col = 0;
//    layout->addWidget(createProfileGroupBox(), row, col++, 1, 1);
//    layout->addWidget(createSelectionGroupBox(), row, col++, 1, 1);
//    layout->addWidget(createBadMarkGroupBox(), row, col++, 1, 1);

    return widget;
}

QWidget *GraphicsLayerSetDialog::createAdvancedTab()
{
    // OpenGL, Drop shadow, Viewport update mode, Paint profiling
    return new QWidget();
}

QDialogButtonBox *GraphicsLayerSetDialog::createButtonBox()
{
    m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|
                                       QDialogButtonBox::Apply|
                                       QDialogButtonBox::Cancel|
                                       QDialogButtonBox::Reset);
    return m_buttonBox;
}

/*
QGroupBox *GraphicsLayerSetDialog::createLayerGroupBox()
{
    auto groupBox = new QGroupBox("Layer set");
    groupBox->setLayout(new QVBoxLayout);
    groupBox->layout()->addWidget(m_view);
    return groupBox;
}

QGroupBox *GraphicsLayerSetDialog::createBackgroundGroupBox()
{
    auto groupBox = new QGroupBox("Background");
    auto layout = new QFormLayout;
    groupBox->setLayout(layout);
    layout->addRow("Brush style", createBrushStyleComboBox(&m_backgroundPalette, GraphicsItemRole::Background));
    layout->addRow("Brush color", createBrushColorComboBox(&m_backgroundPalette, GraphicsItemRole::Background));
    return groupBox;
}

// TODO: Checkbox to enable/disable profile
QGroupBox *GraphicsLayerSetDialog::createProfileGroupBox()
{
    auto groupBox = new QGroupBox("Profile");
    auto layout = new QFormLayout;
    groupBox->setLayout(layout);
    layout->addRow("Brush style", createBrushStyleComboBox(&m_backgroundPalette, GraphicsItemRole::Profile));
    layout->addRow("Brush color", createBrushColorComboBox(&m_backgroundPalette, GraphicsItemRole::Profile));
    layout->addRow("Pen style", createPenStyleComboBox(&m_backgroundPalette, GraphicsItemRole::Profile));
    layout->addRow("Pen color", createPenColorComboBox(&m_backgroundPalette, GraphicsItemRole::Profile));
    return groupBox;
}

QGroupBox *GraphicsLayerSetDialog::createHighlightGroupBox()
{
    auto groupBox = new QGroupBox("Highlight");
    auto layout = new QFormLayout;
    groupBox->setLayout(layout);
    layout->addRow("Brush style", createBrushStyleComboBox(&m_decorationPalette, GisHovered));
    layout->addRow("Brush color", createBrushColorComboBox(&m_decorationPalette, GisHovered));
    layout->addRow("Brush opacity", createOpacitySpinBox());
    layout->addRow("Pen style", createPenStyleComboBox(&m_decorationPalette, GisHovered));
    layout->addRow("Pen color", createPenColorComboBox(&m_decorationPalette, GisHovered));
    return groupBox;
}

QGroupBox *GraphicsLayerSetDialog::createSelectionGroupBox()
{
    auto groupBox = new QGroupBox("Selection");
    auto layout = new QFormLayout;
    groupBox->setLayout(layout);
    layout->addRow("Brush style", createBrushStyleComboBox(&m_decorationPalette, GisSelected));
    layout->addRow("Brush color", createBrushColorComboBox(&m_decorationPalette, GisSelected));
    layout->addRow("Brush opacity", createOpacitySpinBox());
    layout->addRow("Pen style", createPenStyleComboBox(&m_decorationPalette, GisSelected));
    layout->addRow("Pen color", createPenColorComboBox(&m_decorationPalette, GisSelected));
    return groupBox;
}

QGroupBox *GraphicsLayerSetDialog::createGoodMarkGroupBox()
{
    auto groupBox = new QGroupBox("Good Mark");
    auto layout = new QFormLayout;
    groupBox->setLayout(layout);
    layout->addRow("Brush style", createBrushStyleComboBox(&m_decorationPalette, GisMarkedGood));
    layout->addRow("Brush color", createBrushColorComboBox(&m_decorationPalette, GisMarkedGood));
    layout->addRow("Brush opacity", createOpacitySpinBox());
    layout->addRow("Pen style", createPenStyleComboBox(&m_decorationPalette, GisMarkedGood));
    layout->addRow("Pen color", createPenColorComboBox(&m_decorationPalette, GisMarkedGood));
    return groupBox;
}

QGroupBox *GraphicsLayerSetDialog::createBadMarkGroupBox()
{
    auto groupBox = new QGroupBox("Bad Mark");
    auto layout = new QFormLayout;
    groupBox->setLayout(layout);
    layout->addRow("Brush style", createBrushStyleComboBox(&m_decorationPalette, GisMarkedBad));
    layout->addRow("Brush color", createBrushColorComboBox(&m_decorationPalette, GisMarkedBad));
    layout->addRow("Brush opacity", createOpacitySpinBox());
    layout->addRow("Pen style", createPenStyleComboBox(&m_decorationPalette, GisMarkedBad));
    layout->addRow("Pen color", createPenColorComboBox(&m_decorationPalette, GisMarkedBad));
    return groupBox;
}

QDoubleSpinBox *GraphicsLayerSetDialog::createOpacitySpinBox()
{
    auto spinBox = new QDoubleSpinBox();
    spinBox->setMinimum(0.0);
    spinBox->setMaximum(100.0);
    spinBox->setSingleStep(5);
    spinBox->setDecimals(0);
    spinBox->setSuffix(" %");
    spinBox->setValue(100);
    return spinBox;
}

ColorComboBox *GraphicsLayerSetDialog::createBrushColorComboBox(GraphicsItemPalette *palette,
                                                                GraphicsItemRole role)
{
    auto comboBox = new ColorComboBox();
    comboBox->setMinimumContentsLength(8);
    comboBox->setColors(Solarized::colorList());
    comboBox->setMaxVisibleItems(8);
    comboBox->setCurrentColor(palette->style(role).fillColor);
    connect(comboBox, &ColorComboBox::currentColorChanged,
            this, [this, palette, role](const QColor &color)
    {
        GraphicsItemStyle style = palette->style(role);
        style.fillColor = color;
        palette->setStyle(role, style);
        setApplyResetEnabled(true);
    });
    return comboBox;
}

ColorComboBox *GraphicsLayerSetDialog::createBrushColorComboBox(GraphicsItemPalette *palette,
                                                                GraphicsItemState state)
{
    auto comboBox = new ColorComboBox();
    comboBox->setMinimumContentsLength(8);
    comboBox->setColors(Solarized::colorList());
    comboBox->setMaxVisibleItems(8);
    comboBox->setCurrentColor(palette->style(state).fillColor);
    connect(comboBox, &ColorComboBox::currentColorChanged,
            this, [this, palette, state](const QColor &color)
    {
        auto style = palette->style(state);
        style.fillColor = color;
        palette->setStyle(state, style);
        setApplyResetEnabled(true);
    });
    return comboBox;
}

BrushStyleComboBox *GraphicsLayerSetDialog::createBrushStyleComboBox(GraphicsItemPalette *palette,
                                                                     GraphicsItemRole role)
{
    auto comboBox = new BrushStyleComboBox();
    comboBox->setBrushStyles(QList<Qt::BrushStyle>()
                        << Qt::NoBrush
                        << Qt::SolidPattern
                        << Qt::FDiagPattern
                        << Qt::BDiagPattern
                        << Qt::DiagCrossPattern
                        << Qt::CrossPattern);
    comboBox->setCurrentBrushStyle(palette->style(role).fillStyle);
    connect(comboBox, &BrushStyleComboBox::currentBrushStyleChanged,
            this, [this, palette, role](Qt::BrushStyle brushStyle)
    {
        auto style = palette->style(role);
        style.fillStyle = brushStyle;
        palette->setStyle(role, style);
        setApplyResetEnabled(true);
    });
    return comboBox;
}

BrushStyleComboBox *GraphicsLayerSetDialog::createBrushStyleComboBox(GraphicsItemPalette *palette,
                                                                     GraphicsItemState state)
{
    auto comboBox = new BrushStyleComboBox();
    comboBox->setBrushStyles(QList<Qt::BrushStyle>()
                        << Qt::NoBrush
                        << Qt::SolidPattern
                        << Qt::FDiagPattern
                        << Qt::BDiagPattern
                        << Qt::DiagCrossPattern
                        << Qt::CrossPattern);
    comboBox->setCurrentBrushStyle(palette->style(state).fillStyle);
    connect(comboBox, &BrushStyleComboBox::currentBrushStyleChanged,
            this, [this, palette, state](Qt::BrushStyle brushStyle)
    {
        auto style = palette->style(state);
        style.fillStyle = brushStyle;
        palette->setStyle(state, style);
        setApplyResetEnabled(true);
    });
    return comboBox;
}

PenStyleComboBox *GraphicsLayerSetDialog::createPenStyleComboBox(GraphicsItemPalette *palette, GraphicsItemState state)
{
    auto comboBox = new PenStyleComboBox();
    comboBox->setPenStyles(QList<Qt::PenStyle>()
                             << Qt::NoPen
                             << Qt::SolidLine
                             << Qt::DashLine
                             << Qt::DotLine
                             << Qt::DashDotLine
                             << Qt::DashDotDotLine);
    comboBox->setCurrentPenStyle(palette->style(state).outlineStyle);
    connect(comboBox, &PenStyleComboBox::currentPenStyleChanged,
            this, [this, palette, state](Qt::PenStyle penStyle)
    {
        auto style = palette->style(state);
        style.outlineStyle = penStyle;
        palette->setStyle(state, style);
        setApplyResetEnabled(true);
    });
    return comboBox;
}

PenStyleComboBox *GraphicsLayerSetDialog::createPenStyleComboBox(GraphicsItemPalette *palette, GraphicsItemRole role)
{
    auto comboBox = new PenStyleComboBox();
    comboBox->setPenStyles(QList<Qt::PenStyle>()
                             << Qt::NoPen
                             << Qt::SolidLine
                             << Qt::DashLine
                             << Qt::DotLine
                             << Qt::DashDotLine
                             << Qt::DashDotDotLine);
    comboBox->setCurrentPenStyle(palette->style(role).outlineStyle);
    connect(comboBox, &PenStyleComboBox::currentPenStyleChanged,
            this, [this, palette, role](Qt::PenStyle penStyle)
    {
        auto style = palette->style(role);
        style.outlineStyle = penStyle;
        palette->setStyle(role, style);
        setApplyResetEnabled(true);
    });
    return comboBox;
}

ColorComboBox *GraphicsLayerSetDialog::createPenColorComboBox(GraphicsItemPalette *palette, GraphicsItemRole role)
{
    auto comboBox = new ColorComboBox();
    comboBox->setMinimumContentsLength(8);
    comboBox->setColors(Solarized::colorList());
    comboBox->setMaxVisibleItems(8);
    comboBox->setCurrentColor(palette->style(role).outlineColor);
    connect(comboBox, &ColorComboBox::currentColorChanged,
            this, [this, palette, role](const QColor &color)
    {
        GraphicsItemStyle itemStyle = palette->style(role);
        itemStyle.outlineColor = color;
        palette->setStyle(role, itemStyle);
        setApplyResetEnabled(true);
    });
    return comboBox;
}

ColorComboBox *GraphicsLayerSetDialog::createPenColorComboBox(GraphicsItemPalette *palette, GraphicsItemState state)
{
    auto comboBox = new ColorComboBox();
    comboBox->setMinimumContentsLength(8);
    comboBox->setColors(Solarized::colorList());
    comboBox->setMaxVisibleItems(8);
    comboBox->setCurrentColor(palette->style(state).outlineColor);
    connect(comboBox, &ColorComboBox::currentColorChanged,
            this, [this, palette, state](const QColor &color)
    {
        auto style = palette->style(state);
        style.outlineColor = color;
        palette->setStyle(state, style);
        setApplyResetEnabled(true);
    });
    return comboBox;
}
*/
QPainterPath GraphicsLayerSetDialog::padPath()
{
    QPainterPath path;
    path.addRoundRect(0, 4, 32, 24, 25, 25);
    path.translate(-path.boundingRect().center());
    return path;
}

QPainterPath GraphicsLayerSetDialog::pinPath()
{
    QPainterPath path;
    path.addRect(0, 10, 32, 12);
    path.translate(-path.boundingRect().center());
    return path;
}

QPainterPath GraphicsLayerSetDialog::fiducialPath()
{
    QPainterPath path;
    path.addEllipse(8, 8, 16, 16);
    path.addRect(12, 0, 8, 32);
    path.addRect(0, 12, 32, 8);
    path.translate(-path.boundingRect().center());
    return path;
}

QPainterPath GraphicsLayerSetDialog::holePath()
{
    QPainterPath path;
    path.addEllipse(0, 0, 32, 32);
    path.translate(-path.boundingRect().center());
    return path;
}

QPainterPath GraphicsLayerSetDialog::slotPath()
{
    QPainterPath p;
    p.addRoundRect(0, 8, 32, 16, 33, 33);
    p.translate(-p.boundingRect().center());
    return p;
}

QPainterPath GraphicsLayerSetDialog::fillPath()
{
    QPainterPath path;
    path.moveTo(10, 0); path.lineTo(32, 0);
    path.lineTo(32, 20); path.lineTo(10, 20);
    path.lineTo(10, 32); path.lineTo(0, 32);
    path.lineTo(0, 10);
    path.closeSubpath();
    path.translate(-path.boundingRect().center());
    return path;
}

QPainterPath GraphicsLayerSetDialog::tracePath()
{
    QPainterPath path;
    path.addRoundedRect(0, 12, 32, 8, 4, 4); // TODO: corner trace
    path.translate(-path.boundingRect().center());
    return path;
}

QPainterPath GraphicsLayerSetDialog::textPath()
{
    QPainterPath path;
    QFont font;
    font.setPixelSize(28);
    path.addText(0, 0, font, "A");
    path.translate(-path.boundingRect().center());
    return path;
}

void GraphicsLayerSetDialog::restoreGuiState()
{
    QSettings settings;
    settings.beginGroup("GraphicsLayerSetDialog");
    resize(settings.value("size", QSize(600, 400)).toSize());
    move(settings.value("pos", QPoint(200, 200)).toPoint());
    m_tabWidget->setCurrentIndex(settings.value("tab", 0).toInt());
    settings.endGroup();
}

void GraphicsLayerSetDialog::saveGuiState()
{
    QSettings settings;
    settings.beginGroup("GraphicsLayerSetDialog");
    settings.setValue("size", size());
    settings.setValue("pos", pos());
    settings.setValue("tab", m_tabWidget->currentIndex());
    settings.endGroup();
}
